import pandas as pd
import plotly.express as px
from nicegui import ui, app
import hashlib
import os

# 设置中文字体支持（需确保有对应字体文件，可放到 static 目录）
app.add_static_files('/static', '.')

# 模拟数据库存储用户信息
users_db = {}
USER_FILE = 'users.txt'


# 加载用户数据
def load_users():
    global users_db
    if os.path.exists(USER_FILE):
        with open(USER_FILE, 'r', encoding='utf-8') as f:
            for line in f:
                line = line.strip()
                if line and ':' in line:
                    username, password_hash = line.split(':', 1)
                    users_db[username] = password_hash


# 保存用户数据
def save_user(username, password_hash):
    with open(USER_FILE, 'a', encoding='utf-8') as f:
        f.write(f"{username}:{password_hash}\n")


# 密码加密函数
def hash_password(password):
    return hashlib.sha256(password.encode()).hexdigest()


# 数据加载和预处理
def load_and_preprocess_data(file_path):
    try:
        df = pd.read_csv(file_path)
        # 数据清洗
        df['Undernourished'] = pd.to_numeric(df['Undernourished'], errors='coerce')
        numeric_cols = df.select_dtypes(include='number').columns
        df[numeric_cols] = df[numeric_cols].apply(lambda x: x.fillna(x.mean()))
        df = df.drop_duplicates()
        return df
    except Exception as e:
        ui.notify(f"数据加载失败: {str(e)}", type='negative')
        return None


# 图表创建函数
def create_2d_scatter(df):
    fig = px.scatter(
        df,
        x='Obesity',
        y='Undernourished',
        hover_name='Country',
        color='Population',
        size='Population',
        size_max=60,
        labels={
            'Obesity': '肥胖率 (%)',
            'Undernourished': '营养不良率 (%)',
            'Population': '人口'
        },
        title='肥胖率与营养不良率的关系'
    )
    fig.update_layout(
        height=600,
        font=dict(family="SimHei, WenQuanYi Micro Hei, Heiti TC"),
        plot_bgcolor='rgba(240, 240, 240, 0.9)'
    )
    return fig


def create_3d_scatter(df):
    fig = px.scatter_3d(
        df,
        x='Obesity',
        y='Undernourished',
        z='Population',
        color='Vegetal Products',
        size='Animal fats',
        size_max=40,
        hover_name='Country',
        labels={
            'Obesity': '肥胖率 (%)',
            'Undernourished': '营养不良率 (%)',
            'Population': '人口'
        },
        title='肥胖率、营养不良率与人口的关系'
    )
    fig.update_layout(
        height=600,
        font=dict(family="SimHei, WenQuanYi Micro Hei, Heiti TC"),
        margin=dict(l=0, r=0, b=0, t=40)
    )
    return fig


def create_bar_chart(df, top_n=20, category='Population'):
    if category == 'Population':
        top_countries = df.sort_values(category, ascending=False).head(top_n)
        values = top_countries[category] / 1e6
        y_title = '人口 (百万)'
    else:
        top_countries = df.sort_values(category, ascending=False).head(top_n)
        values = top_countries[category]
        y_title = f'{category} (%)'

    fig = px.bar(
        top_countries,
        x='Country',
        y=values,
        color=values,
        color_continuous_scale='Viridis',
        title=f'按{category}排名前{top_n}的国家'
    )
    fig.update_layout(
        height=600,
        font=dict(family="SimHei, WenQuanYi Micro Hei, Heiti TC"),
        xaxis_tickangle=45
    )
    return fig


def create_correlation_heatmap(df):
    columns = [
        'Obesity', 'Undernourished', 'Population',
        'Animal fats', 'Vegetal Products'
    ]
    corr_matrix = df[columns].corr()
    fig = px.imshow(
        corr_matrix,
        text_auto=True,
        color_continuous_scale='RdBu_r',
        range_color=[-1, 1],
        title="关键指标相关性热图"
    )
    fig.update_layout(
        height=600,
        font=dict(family="SimHei, WenQuanYi Micro Hei, Heiti TC")
    )
    return fig


# -------------------- 页面路由定义 -------------------- #
@ui.page('/')  # 登录页路由
def build_login_page():
    load_users()  # 加载用户数据

    with ui.card().classes('absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 w-96 p-6'):
        ui.label('用户登录').classes('text-2xl font-bold mb-6 text-center')

        username = ui.input(label='用户名').classes('mb-4')
        password = ui.input(label='密码', password=True).classes('mb-4')
        message = ui.label('').classes('text-red-500 mb-4 text-center')

        def handle_login():
            if not username.value or not password.value:
                message.text = '请输入用户名和密码'
                return

            user_hash = users_db.get(username.value)
            if user_hash and user_hash == hash_password(password.value):
                message.text = '登录成功，正在进入系统...'
                message.classes('text-green-500')
                # 延迟 1 秒跳转至仪表盘
                ui.timer(1.0, lambda: ui.navigate.to('/dashboard'), once=True)
            else:
                message.text = '用户名或密码错误'

        ui.button('登录', on_click=handle_login).classes('w-full mb-2')
        ui.button('注册', on_click=lambda: ui.navigate.to('/register')).classes('w-full')


@ui.page('/register')  # 注册页路由
def build_register_page():
    with ui.card().classes('absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 w-96 p-6'):
        ui.label('用户注册').classes('text-2xl font-bold mb-6 text-center')

        reg_user = ui.input(label='用户名').classes('mb-4')
        reg_pass = ui.input(label='密码', password=True).classes('mb-4')
        reg_msg = ui.label('').classes('text-red-500 mb-4 text-center')

        def handle_register():
            if not reg_user.value or not reg_pass.value:
                reg_msg.text = '请输入用户名和密码'
                return
            if reg_user.value in users_db:
                reg_msg.text = '用户名已存在'
                return

            # 保存新用户
            save_user(reg_user.value, hash_password(reg_pass.value))
            users_db[reg_user.value] = hash_password(reg_pass.value)
            reg_msg.text = '注册成功，正在返回登录...'
            reg_msg.classes('text-green-500')

            # 延迟 2 秒跳转至登录页
            ui.timer(2.0, lambda: ui.navigate.to('/'), once=True)

        ui.button('注册', on_click=handle_register).classes('w-full mb-2')
        ui.button('返回登录', on_click=lambda: ui.navigate.to('/')).classes('w-full')


@ui.page('/dashboard')  # 仪表盘路由
def build_dashboard_page():
    df = load_and_preprocess_data('Food_Supply_Quantity_kg_Data.csv')
    if df is None:
        ui.label('无法加载数据，请检查文件路径').classes('text-red-500')
        ui.button('返回登录', on_click=lambda: ui.navigate.to('/')).classes('mt-4')
        return

    with ui.header().classes('bg-blue-600 text-white p-4'):
        ui.label('全球食品供应与健康数据分析').classes('text-2xl font-bold')
        with ui.row().classes('ml-auto'):
            ui.button('刷新数据', on_click=lambda: refresh_data(df)).classes('mr-2')
            ui.button('退出登录', on_click=lambda: ui.navigate.to('/')).classes('mr-2')

    with ui.tabs().classes('w-full') as tabs:
        ui.tab('2D 散点图')
        ui.tab('3D 散点图')
        ui.tab('柱状图')
        ui.tab('相关性分析')
        ui.tab('数据表格')

    with ui.tab_panels(tabs, value='2D 散点图').classes('p-4'):
        with ui.tab_panel('2D 散点图'):
            scatter_2d = ui.plotly(create_2d_scatter(df)).classes('w-full h-[600px]')

        with ui.tab_panel('3D 散点图'):
            scatter_3d = ui.plotly(create_3d_scatter(df)).classes('w-full h-[600px]')

        with ui.tab_panel('柱状图'):
            with ui.row().classes('mb-4'):
                category_select = ui.select(
                    options=['Population', 'Obesity', 'Undernourished', 'Animal fats', 'Vegetal Products'],
                    value='Population'
                ).classes('w-1/4')
                top_n_slider = ui.slider(min=5, max=50, value=20).classes('w-1/4')
                update_btn = ui.button('更新', on_click=lambda: update_bar())

            bar_chart = ui.plotly(create_bar_chart(df)).classes('w-full h-[600px]')

            def update_bar():
                bar_chart.figure = create_bar_chart(df, int(top_n_slider.value), category_select.value)
                bar_chart.update()

        with ui.tab_panel('相关性分析'):
            heatmap = ui.plotly(create_correlation_heatmap(df)).classes('w-full h-[600px]')

        with ui.tab_panel('数据表格'):
            columns = [
                {'name': 'Country', 'label': '国家', 'field': 'Country'},
                {'name': 'Population', 'label': '人口', 'field': 'Population'},
                {'name': 'Obesity', 'label': '肥胖率 (%)', 'field': 'Obesity'},
                {'name': 'Undernourished', 'label': '营养不良率 (%)', 'field': 'Undernourished'}
            ]
            data_table = ui.table(columns=columns, rows=df.to_dict('records')).classes('w-full')

    async def refresh_data(current_df):
        with ui.spinner(size='xl'):
            await ui.run_javascript('console.log("开始刷新数据...")')  # 调试用
            new_df = load_and_preprocess_data('Food_Supply_Quantity_kg_Data.csv')
            if new_df is not None:
                scatter_2d.figure = create_2d_scatter(new_df)
                scatter_2d.update()
                scatter_3d.figure = create_3d_scatter(new_df)
                scatter_3d.update()
                bar_chart.figure = create_bar_chart(new_df)
                bar_chart.update()
                heatmap.figure = create_correlation_heatmap(new_df)
                heatmap.update()
                data_table.rows = new_df.to_dict('records')
                data_table.update()
                ui.notify('数据已刷新', type='positive')


# -------------------- 启动应用 -------------------- #
if __name__ in {"__main__", "__mp_main__"}:
    # 初始化用户数据
    load_users()
    # 启动 NiceGUI
    ui.run(title='全球食品供应与健康数据分析', dark=True, port=8080)